/*
 * wireless.c
 *
 * Created: 2013/12/19 14:25:00
 *  Author: alivehex
 */ 
#include <stdio.h>
#include <string.h>

#include "hardware.h"
#include "wireless.h"
#include "uart.h"
#include "gpio.h"
#include "cbuf.h"

typedef enum {
	PTX_STATE_IDLE,
	PTX_STATE_TX,
} ptx_state_t;

typedef struct {
	// uint16_t ubrr;
	// int parity;
	// int stopBits;
	// int dataBits;
	int rfChannel;
	int rfDataRate;
	int rfPwr;
	int sysRole;
	int sum;
} sys_parameters_t;

#define PTX_STATUS_MAXRT	(1 << 0)

#define LOST_PRX_COUNT_MAX		0x1FFFF

#define LED_ON()	_CLR_IO(LED_PORT, LED_PIN)
#define LED_OFF()	_SET_IO(LED_PORT, LED_PIN)
#define LED_BLINK()	_PORT_(LED_PORT) ^= LED_PIN;

#define PTX_UTX_CACHE_SIZEB		32
#define PTX_URX_CACHE_SIZEB		32

//static char PTX_UTX_BUF[PTX_UTX_CACHE_SIZEB];
static char PTX_URX_BUF[PTX_URX_CACHE_SIZEB];

// static int PTX_RESP_LENB = 0;
// cbuf_t ptxUTxBuffer;
cbuf_t ptxURxBuffer;

static volatile ptx_state_t PTX_STATE = PTX_STATE_IDLE;
//static rf_ctrl_package_t RF_CTRL_PKG;

uint8_t PTX_STATUS = 0;
/* Control package header */
const static char RF_HEADER[4] = {'R','F','?','!'};

// static uint8_t RF_CHANNEL = RF_DEFAULT_CHANNEL;
static volatile sys_role_t RF_ROLE = SYS_ROLE_PTX;

static void(*TxEndCallback)(int err) = NULL;
static void(*RxEndCallback)(char *buffer, int length) = NULL;

/* 1 = PTX, 0 = PRX */
// #define GET_SYSTEM_ROLE()
//	RF_ROLE = _GET_IO(ROLE_PORT, ROLE_PIN) ? SYS_ROLE_PTX : SYS_ROLE_PRX;

static void setupRFChannel(int rfChannel);
static void setupRFPTXAddress(char address[5]);
static void setupRFPRXAddress(char address[5]);
// static void ptxWirelessPoll(void);
// static void prxWirelessPoll(void);
// static void loadFromE2prom(sys_parameters_t *ptr);
// static void store2E2prom(sys_parameters_t *ptr);
//extern int PRX_package_handler(const char *buffer, int len);
extern void prxRFPackageHandler(const char *buffer, int length, char *rfACKBuffer, int *rfACKLength);


/* Basic SPI read and write function */
static uint8_t spiReadWriteSW(uint8_t data) {
	uint8_t buf = 0;
	uint8_t i;

	for(i = 0; i < 8; i++) {
		if(data & 0x80) { // MSB
			_SET_IO(MOSI_PORT, MOSI_PIN);
		} else {
			_CLR_IO(MOSI_PORT, MOSI_PIN);
		}
		
		_SET_IO(SPCK_PORT, SPCK_PIN);
		
		data <<= 1;
		// receive data
		buf <<= 1;
		if(_GET_IO(MISO_PORT, MISO_PIN)) {
			buf |= 1;
		}
		
		_CLR_IO(SPCK_PORT, SPCK_PIN);
	}
	return buf;
}

/* nRF24L01 read and write function with command */
static int wirelessRW(uint8_t command, const char *writeBuffer, char *readBuffer, int bufferLengthByte) {
	uint8_t status;
	uint8_t spiData;
	
	_CLR_IO(NPCS_PORT, NPCS_PIN);
	
	status = spiReadWriteSW(command);
	while (bufferLengthByte --) {
		if(writeBuffer) {
			spiData = spiReadWriteSW(*writeBuffer ++);
		} else {
			spiData = spiReadWriteSW(0xFF);
		}
		if(readBuffer) {
			*readBuffer ++ = spiData;
		}
	}
	
	_SET_IO(NPCS_PORT, NPCS_PIN);
	return status;
}

/* Read one byte from nRF24L01 register */
void wirelessWriteReg(uint8_t address, uint8_t value) {
	 wirelessRW(address | 0x20, (const char *)&value, NULL, 1);
}

/* Write one byte to nRF24L01 register */
uint8_t wirelessReadReg(uint8_t address) {
	char buffer;
	
	wirelessRW(address, NULL, &buffer, 1);
	return buffer;
}

/* Write data to WFIFO and enable transfer */
//void wirelessWrite(const char *buffer, int bufferLength) {
//	while(PTX_STATE == PTX_STATE_TX);
//	
//	_CLR_IO(CE_PORT, CE_PIN);
//	wirelessRW(0xA0, buffer, NULL, bufferLength);
//	PTX_STATE = PTX_STATE_TX;
//	
//	_SET_IO(CE_PORT, CE_PIN);
//	_delay_us(50);
//	_CLR_IO(CE_PORT, CE_PIN);
//}

void wirelessWriteAsync(const char serialNumber[6], const char *data, int length)
{
	_CLR_IO(CE_PORT, CE_PIN);
	
	setupRFChannel(serialNumber[0]);
	setupRFPTXAddress((char *)&serialNumber[1]);
	setupRFPRXAddress((char *)&serialNumber[1]);
	
	wirelessRW(0xA0, data, NULL, length);
	PTX_STATE = PTX_STATE_TX;
	
	_SET_IO(CE_PORT, CE_PIN);
	_delay_us(15);
	_CLR_IO(CE_PORT, CE_PIN);
}

// 0 = everything is fine
// 1 = timeout
// 2 = no response from the remote device
int wirelessWriteSync(const char prxSerialNumber[6], const char *buffer, int len) {
	volatile unsigned int timeout_ms = 20;
	
	_CLR_IO(CE_PORT, CE_PIN);
#if (0)
	// flush RX-FIFO
	wirelessRW(0xE2, NULL, NULL, 0);
	// flush TX-FIFO
	wirelessRW(0xE1, NULL, NULL, 0);
#endif
	
	setupRFChannel(prxSerialNumber[0]);
	setupRFPTXAddress((char *)&prxSerialNumber[1]);
	
	wirelessRW(0xA0, buffer, NULL, len);	
	PTX_STATE = PTX_STATE_TX;
	
	_SET_IO(CE_PORT, CE_PIN);
	_delay_us(15);
	_CLR_IO(CE_PORT, CE_PIN);
	
	do {
		if (PTX_STATE != PTX_STATE_TX) {
			break;
		}
		_delay_ms(1);
	} while (-- timeout_ms);
	
	if (timeout_ms == 0) {
		DBG("timeout");
		return RF_RET_TIMEOUT;
	}
	
	if (PTX_STATUS & PTX_STATUS_MAXRT) {
		DBG("MAXRT");
		return RF_RET_PRXNRSP;
	}
	
	return RF_RET_PASS;
}

void setTxEndCallback(void *function)
{
	TxEndCallback = function;
}

void setRxEndCallback(void *function)
{
	RxEndCallback = function;
}

int isPRXResponse(void)
{
	return (PTX_STATUS & PTX_STATUS_MAXRT) ? 0 : 1;
}

void writePRXAckPayload(const char *buffer, int length)
{
	wirelessRW(0xA8, buffer, NULL, length);
}

static void setupRFChannel(int rfChannel) {
	wirelessWriteReg(0x5, rfChannel);
}

static void setupRFDataRateAndPowerLevel(int rfDataRate, int rfPowerLevel) {
	if (rfDataRate == 2) {
		// low 250Kbps settings
		wirelessWriteReg(0x6, (rfPowerLevel << 1) | (1 << 0));
	} else {
		wirelessWriteReg(0x6, (rfDataRate << 3) | (rfPowerLevel << 1) | (1 << 0));
	}
}

static void setupRFPRXAddress(char address[5]) {
	wirelessRW(0x20 | 0x0a, address, NULL, 5);
}

static void setupRFPTXAddress(char address[5]) {
	wirelessRW(0x20 | 0x10, address, NULL, 5);
}

sys_role_t getRFSystemRole() {
	io_input_hiz(ROLE_MODE_PORT, ROLE_MODE_PIN);
	
	_delay_ms(1);
	
	if (io_get(ROLE_MODE_PORT, ROLE_MODE_PIN))
	{
		return SYS_ROLE_PRX;
	} else {
		return SYS_ROLE_PTX;
	}
}

// void PTXDeviceInit(uint8_t rfChannel) {
void PTXDeviceInit(int rfChannel, int rfDataRate, int rfPowerLevel) {
	char buffer[1];
	int retryLimit = 10;
	
	// stop here if nRF24L01 module is missing
	while(-- retryLimit) {
		// enable the device and enter PTX mode
		wirelessWriteReg(0, (1 << 3) | (1 << 1));
		
		if(wirelessReadReg(0) == ((1 << 3) | (1 << 1))) {
			uart1_puts("PTX\r\n");
			break;
		} else {
			_delay_ms(50);
		}
	}
	if(retryLimit == 0) {
		uart1_puts("PTX ERR\r\n");
		return;
	}
	
	// flush the TX-FIFO
	wirelessRW(0xE1, NULL, NULL, 0);
	// clear all interrupt bits
	wirelessWriteReg(0x7, 0xFF);
	// set RF-channel
	setupRFChannel(rfChannel);
	// set RF-DR, 0dbm and RF-PW
	setupRFDataRateAndPowerLevel(rfDataRate, rfPowerLevel);
	// to enable the ACK-payload data more then 15B
	wirelessWriteReg(0x4, (2 << 4) | (15 << 0));
	// enable the DYNPD and FEATURE register
	buffer[0] = 0x73;
	wirelessRW(0x50, buffer, NULL, 1);
	// enable the R_RX_PL_WID, W_ACK_PAYLOAD, disable W_TX_PAYLOAD_NOACK
	wirelessWriteReg(0x1D, (1 << 2) | (1 << 1));
	// wirelessWriteReg(0x1D, (1 << 1));
	// enable the dynamic package length in all pipes
	wirelessWriteReg(0x1C, 1);
	
	_delay_ms(20);
}

// void PRXDeviceInit(uint8_t channel, uint8_t rfDR) {
void PRXDeviceInit(int rfChannel, int rfDataRate, int rfPowerLevel, char rfAddress[5]) {
	char buffer[1];
	int retryLimit = 10;
		
	_delay_ms(10);

	_CLR_IO(CE_PORT, CE_PIN);
	// stop here if nRF24L01 module is missing
	while(-- retryLimit) {
		// enable the device and enter PRX mode
		wirelessWriteReg(0, (1 << 3) | (1 << 1) | (1 << 0));
		
		if(wirelessReadReg(0) == ((1 << 3) | (1 << 1) | (1 << 0))) {
			uart1_puts("PRX\r\n");
			break;
		} else {
			_delay_ms(50);
		}
	}	
	if(retryLimit == 0) {
		uart1_puts("PRX ERR\r\n");
		return;
	}
	// flush the RX-FIFO
	wirelessRW(0xE2, NULL, NULL, 0);
	// clear all interrupt bits
	wirelessWriteReg(0x7, 0xFF);
	// set RF-channel
	setupRFChannel(rfChannel);
	// set RF-DR, 0dbm and LNA gain
	setupRFDataRateAndPowerLevel(rfDataRate, rfPowerLevel);
	// set PRX-Address
	setupRFPRXAddress(rfAddress);
	// to enable the ACK-payload data more then 15B
	wirelessWriteReg(0x4, (2 << 4) | (15 << 0));
	// enable the DYNPD and FEATURE register
	buffer[0] = 0x73;
	wirelessRW(0x50, buffer, NULL, 1);
	// enable the R_RX_PL_WID, W_ACK_PAYLOAD, disable W_TX_PAYLOAD_NOACK
	wirelessWriteReg(0x1D, (1 << 2) | (1 << 1));
	// wirelessWriteReg(0x1D, (1 << 1));
	// enable the dynamic package length in all pipes
	wirelessWriteReg(0x1C, 1);
		
	_delay_ms(20);
	
	// listen to packages in the air
	_SET_IO(CE_PORT, CE_PIN);
}

int wirelessReadCount() {
	return getDataCount(&ptxURxBuffer);
}

int wirelessRead(char *buffer, int len) {
	return CBufRead(&ptxURxBuffer, buffer, len);
}

// sysRole = SYS_ROLE_PTX, SYS_ROLE_PRX
// rfDataRate = 0(1Mbps), 1(2Mbps), 2(250Kpbs)
// rfPowerLevel = 0(Min) - 3(Max)
int wirelessInit(int rfChannel, sys_role_t sysRole, int rfDataRate, int rfPowerLevel, char rfAddress[5]) {
	// Config the GPIOs
	// unsigned long baudrate;
	// uint8_t rfChannel;
	// sys_parameters_t sys;
	WIRELESS_LOWLEVEL_INIT();
	
	// _CONFIG_IO_INPUT_PULLUP(ROLE_MODE_PORT, ROLE_MODE_PIN);

	//sys.rfChannel = RF_DEFAULT_CHANNEL;
	//sys.sysRole = SYS_ROLE_PTX;
	//sys.rfDataRate = 0;
	//sys.rfPwr = 3; // Maxim

	// baudrate = UART_DEFAULT_BAUDRATE; // getSystemBaudrate();
	// rfChannel = 0;	//getSystemRFChannel();
	// GET_SYSTEM_ROLE();
	//RF_ROLE = sys.sysRole;
	RF_ROLE = sysRole;
	
	// 0 = PTX, 1 = PRX
	//if (_GET_IO2(ROLE_MODE_PORT, ROLE_MODE_PIN)) {
	//	RF_ROLE = SYS_ROLE_PTX;
	//} else {
	//	RF_ROLE = SYS_ROLE_PRX;
	//}
	
	// uartOpen(baudrate, 8, 1, 0);
	// uart1_open(UART_DEFAULT_BAUDRATE);
	// uart1_open2(sys.ubrr, sys.dataBits, sys.stopBits, sys.parity);
	// kprintf("ROLE=%d\r\n", RF_ROLE);
	
	// EIMSK = (1 << 2);
	// enable the interrupt line
	GICR |= (1 << 5);

	// CBufInit(&ptxUTxBuffer, PTX_UTX_BUF, PTX_UTX_CACHE_SIZEB);
	CBufInit(&ptxURxBuffer, PTX_URX_BUF, PTX_URX_CACHE_SIZEB);
	
	// Disable CE to write nRF24L01 register
	_CLR_IO(CE_PORT, CE_PIN);
	
	if(RF_ROLE == SYS_ROLE_PTX)
	{
		PTXDeviceInit(rfChannel, rfDataRate, rfPowerLevel);
	}
	else
	{
		PRXDeviceInit(rfChannel, rfDataRate, rfPowerLevel, rfAddress);
	}
	
	// enable global interrupt
	// sei();
	// do this in Main function
	return RF_ROLE;
}

// RF interrupt handler
ISR(INT2_vect) {
	uint8_t status;
	uint8_t dataCount;
	char buffer[RF_CACHE_SIZEB];
	char prxAckBuffer[RF_CACHE_SIZEB];
	//static uint8_t ledBlinkCount = 0;
	// char ret;
	int nbytes;
	// int i;
	// int pipeIndex;
	
	// must check the INT2 pin value here
	
	_CLR_IO(CE_PORT, CE_PIN);
	
	status = wirelessRW(0xFF, NULL, NULL, 0);
	wirelessWriteReg(0x7, 0xFF);
	
	// kprintf("INT(0x%x)", status);
	// Re-enable the global interrupt
	// very important because without this opt,
	// TXI and RXI will not have enough time to do their job
	sei();
	
	if(status == 0)
	{
		return;
	}
	
	if(status & (1 << 0))
	{
		DBG("[TX_FULL]");
		// nothing to do here
		if (TxEndCallback != NULL)
		{
			TxEndCallback(2);
		}
	}
	
	if( ((status >> 1) & 7) < 6 )
	{
		// pipeIndex = (status >> 1) & 7;
		// DBG("[RX_P_NO=%d]", pipeIndex);
		// pipeIndex is reserved for future use
	}
	
	if( status & (1 << 4) )
	{
		DBG("[MAX_RT]");
		if(RF_ROLE == SYS_ROLE_PTX)
		{
			// flush the TX FIFO
			// Max retry interrupt, this means PRX is missing
			wirelessRW(0xE1, NULL, NULL, 0);
			PTX_STATE = PTX_STATE_IDLE;
			PTX_STATUS |= PTX_STATUS_MAXRT;
			LED_OFF();
			if (TxEndCallback != NULL)
			{
				TxEndCallback(1);
			}
			// blink the LED fast
			//ledBlinkCount = (ledBlinkCount + 1) % 80;
			//switch(ledBlinkCount) {
			//case 0: LED_ON();  break;
			//case 5:	LED_OFF(); break;
			//}
			//DBG("[MAX_RT STATE=%d]", PTX_STATE);
		}
		else
		{
			// nothing for PRX here
		}
	}
	
	if( status & (1 << 5) )
	{
		DBG("[TX_DS]");
		if(RF_ROLE == SYS_ROLE_PTX)
		{
			// If there is no data in PRX ACK payload,
			// Data Sent interrupt is asserted.
			//DBG("[TX_DS STATE=%d]", PTX_STATE);
			PTX_STATE = PTX_STATE_IDLE;
			PTX_STATUS &= ~PTX_STATUS_MAXRT;
			LED_ON();
			if (TxEndCallback != NULL)
			{
				TxEndCallback(0);
			}
		}
		else
		{
			// nothing for PRX here
		}
	}
	
	if( status & (1 << 6) )
	{
		DBG("[RXDR]");
		// read data length
		wirelessRW(0x60, NULL, (char *)&dataCount, 1);
		// read data
		wirelessRW(0x61, NULL, buffer, dataCount);
		
		if(RF_ROLE == SYS_ROLE_PTX)
		{
			// ACK data from the PRX device
			// write data to cache for TXE_READ
			// kprintf("RCV %d 0x%x\r\n", dataCount, buffer[0]);			
			CBufWrite(&ptxURxBuffer, buffer, dataCount);
			// enable the TXE interrupt
			// UCSR0B |= (1 << 5);
			
			PTX_STATE = PTX_STATE_IDLE;
			PTX_STATUS &= ~PTX_STATUS_MAXRT;
			LED_ON();
		}
		else if(RF_ROLE == SYS_ROLE_PRX)
		{
			if (RxEndCallback != NULL)
			{
				RxEndCallback(buffer, dataCount);
			}
			// write ACK payload data
			// dataCount = CBufRead(&ptxUTxBuffer, buffer, sizeof(buffer));
			// if(dataCount) {
				// RCNT += dataCount;
				// kprintf("%lu\r\n", RCNT);
				// wirelessRW(0xA8, buffer, NULL, dataCount);
			//}
		} else
		{
			cli();
			uart1_puts("PTRX ERR");
			while (1);
		}
	}
	
	if(RF_ROLE == SYS_ROLE_PRX)
	{
		// listen packages in the air
		_SET_IO(CE_PORT, CE_PIN);
	}
	// DBG("\r\n");
}
